home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Visual Cafe 3
/
Visual Cafe 3.ISO
/
Vcafe
/
JFC.bin
/
BasicTableUI.java
< prev
next >
Wrap
Text File
|
1998-06-30
|
26KB
|
766 lines
/*
* @(#)BasicTableUI.java 1.49 98/04/09
*
* Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
*
* This software is the confidential and proprietary information of Sun
* Microsystems, Inc. ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Sun.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
* SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
* THIS SOFTWARE OR ITS DERIVATIVES.
*
*/
package com.sun.java.swing.plaf.basic;
import com.sun.java.swing.table.*;
import com.sun.java.swing.*;
import com.sun.java.swing.event.*;
import java.util.Enumeration;
import java.awt.event.*;
import java.awt.*;
import com.sun.java.swing.plaf.*;
import java.io.Serializable;
import java.util.EventObject;
import com.sun.java.swing.text.*;
/**
* BasicTableUI implementation
* <p>
* Warning: serialized objects of this class will not be compatible with
* future swing releases. The current serialization support is appropriate
* for short term storage or RMI between Swing1.0 applications. It will
* not be possible to load serialized Swing1.0 objects with future releases
* of Swing. The JDK1.2 release of Swing will be the compatibility
* baseline for the serialized form of Swing objects.
*
* @version 1.49 04/09/98
* @author Philip Milne
* @author Alan Chung
*/
public class BasicTableUI extends TableUI implements MouseListener,
MouseMotionListener, FocusListener, Serializable
{
//
// Instance Variables
//
/** The JTable this UI is hooked up to */
protected JTable table;
// PENDING(philip): Workaround for mousePressed bug in AWT 1.1
private boolean phantomMousePressed = false;
private KeyListener tableKeyListener;
/** Component that will recieve mouse events while editing. Not
* necessarily the editorComponent. */
transient protected Component dispatchComponent;
protected CellRendererPane rendererPane;
//
// Install/Deinstall UI
//
// An anonymous inner class would be better here, but we want to
// keep all the methods this calls (eg. moveAnchor) private -
// as this part of the implementation is likely to change.
private class ArrowKeyAction extends AbstractAction implements Serializable {
transient protected int dx;
transient protected int dy;
public ArrowKeyAction(int dx, int dy) {
this.dx = dx;
this.dy = dy;
}
public boolean inRange(int index, int max) { return index >= 0 && index < max; }
public void actionPerformed(ActionEvent e) {
int anchorColumn = table.getSelectedColumn() + dx;
if (!inRange(anchorColumn, table.getColumnCount())) {
return;
}
int anchorRow = table.getSelectedRow() + dy;
if (!inRange(anchorRow, table.getRowCount())) {
return;
}
if (dx != 0) {
clearSelection(table.getColumnModel().getSelectionModel());
}
if (dy != 0) {
clearSelection(table.getSelectionModel());
}
moveAnchor(anchorRow, anchorColumn);
}
}
private void registerArrowKey(int keyEvent, int dx, int dy) {
table.registerKeyboardAction(new ArrowKeyAction(dx, dy),
KeyStroke.getKeyStroke(keyEvent, 0), JComponent.WHEN_FOCUSED);
}
private void registerKeyboardActions()
{
registerArrowKey(KeyEvent.VK_RIGHT, 1, 0);
registerArrowKey(KeyEvent.VK_LEFT , -1, 0);
registerArrowKey(KeyEvent.VK_UP, 0, -1);
registerArrowKey(KeyEvent.VK_DOWN , 0, 1);
tableKeyListener = new TableKeyListener();
table.addKeyListener(tableKeyListener);
}
private void unregisterKeyboardActions() {
table.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0));
table.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0));
table.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0));
table.unregisterKeyboardAction(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0));
table.removeKeyListener(tableKeyListener);
}
private void moveAnchor(int row, int column) {
if (table.isEditing()) {
TableCellEditor cellEditor = table.getCellEditor();
// Try to stop the current editor
if (cellEditor != null) {
boolean stopped = cellEditor.stopCellEditing();
if (!stopped)
return; // The current editor not resigning
}
}
updateSelection(row, column, false, true);
}
private class TableKeyListener implements KeyListener, Serializable
{
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
break;
case KeyEvent.VK_DOWN:
break;
case KeyEvent.VK_LEFT:
break;
case KeyEvent.VK_RIGHT:
break;
case KeyEvent.VK_SHIFT:
break;
case KeyEvent.VK_CONTROL:
break;
default:
dispatchKeyboardEvent(e);
break;
}
}
public void keyReleased(KeyEvent e) { }
public void keyTyped(KeyEvent e) { }
}
private void dispatchKeyboardEvent(KeyEvent e) {
int selectedRow = table.getSelectedRow();
int selectedColumn = table.getSelectedColumn();
if (selectedRow != -1 && selectedColumn != -1 && !table.isEditing()) {
boolean editing = table.editCellAt(selectedRow, selectedColumn);
table.requestFocus();
if (!editing) {
return;
}
}
Component editorComp = table.getEditorComponent();
if (table.isEditing() && editorComp != null) {
// Have to give the textField the focus temporarily so
// that it can perform the action.
char keyChar = e.getKeyChar();
if (editorComp instanceof JTextField) {
JTextField textField = (JTextField)editorComp;
Keymap keyMap = textField.getKeymap();
KeyStroke key = KeyStroke.getKeyStroke(keyChar, 0);
Action action = keyMap.getAction(key);
if (action == null) {
action = keyMap.getDefaultAction();
}
if (action != null) {
ActionEvent ae = new ActionEvent(textField,
ActionEvent.ACTION_PERFORMED,
String.valueOf(keyChar));
action.actionPerformed(ae);
// e.consume();
}
}
}
}
/**
* Initialize JTable properties, e.g. font, foreground, and background.
* The font, foreground, and background
* properties are only set if their current value is either null
* or a UIResource, other properties are set if the current
* value is null.
*
* @see #installUI
*/
private void configureTable()
{
// list.setLayout(null);
LookAndFeel.installColorsAndFont(table, "Table.background",
"Table.foreground", "Table.font");
Color sbg = table.getSelectionBackground();
if (sbg == null || sbg instanceof UIResource) {
table.setSelectionBackground(UIManager.getColor("Table.selectionBackground"));
}
Color sfg = table.getSelectionForeground();
if (sfg == null || sfg instanceof UIResource) {
table.setSelectionForeground(UIManager.getColor("Table.selectionForeground"));
}
Color gridColor = table.getGridColor();
if (gridColor == null || gridColor instanceof UIResource) {
table.setGridColor(UIManager.getColor("Table.gridColor"));
}
// install the scrollpane border
Container parent = table.getParent(); // should be viewport
if (parent != null) {
parent = parent.getParent(); // should be the scrollpane
if (parent != null && parent instanceof JScrollPane) {
LookAndFeel.installBorder((JScrollPane)parent, "Table.scrollPaneBorder");
}
}
}
public static ComponentUI createUI(JComponent c) {
return new BasicTableUI();
}
public void installUI(JComponent c) {
table = (JTable)c;
c.addMouseListener(this);
c.addMouseMotionListener(this);
c.addFocusListener(this);
rendererPane = new CellRendererPane();
table.add(rendererPane);
configureTable();
registerKeyboardActions();
}
public void uninstallUI(JComponent c) {
table.remove(rendererPane);
c.removeMouseListener(this);
c.removeMouseMotionListener(this);
c.removeFocusListener(this);
unregisterKeyboardActions();
rendererPane = null;
table = null;
}
//
// MouseListener, MouseMotionListener, FocusListener Methods
//
private void repaintAnchorCell() {
int anchorRow = table.getSelectedRow();
int anchorColumn = table.getSelectedColumn();
Rectangle dirtyRect = table.getCellRect(anchorRow, anchorColumn, false);
table.repaint(dirtyRect);
}
public void focusGained(FocusEvent e) {
if (table.getSelectedColumn() == -1) {
table.setColumnSelectionInterval(0, 0);
}
if (table.getSelectedRow() == -1) {
table.setRowSelectionInterval(0, 0);
}
repaintAnchorCell();
}
public void focusLost(FocusEvent e) {
repaintAnchorCell();
}
public void mouseMoved(MouseEvent e) {
if (dispatchComponent != null)
dispatchComponent = null;
}
public void mouseClicked(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {
if (dispatchComponent != null)
dispatchComponent = null;
}
public void mouseExited(MouseEvent e) {
if (dispatchComponent != null)
dispatchComponent = null;
}
public void mousePressed(MouseEvent e) {
if (phantomMousePressed == true) {
// System.err.println("BasicTableUI recieved two consecutive mousePressed events.");
return;
}
phantomMousePressed = true;
Point p = e.getPoint();
int hitRowIndex = table.rowAtPoint(p);
int hitColumnIndex = table.columnAtPoint(p);
if ((hitColumnIndex == -1) || (hitRowIndex == -1)) {
// Didn't hit a valid cell
return;
}
Rectangle cellRect = table.getCellRect(hitRowIndex, hitColumnIndex, false);
if(cellRect.contains(p)) {
table.editCellAt(hitRowIndex, hitColumnIndex, e);
}
if(!table.isEditing()) {
table.requestFocus();
// We hit the margin around a cell, or the cell hit didn't start
// the editor. So we can now handle it as a selection click.
boolean controlKeyDown = e.isControlDown();
boolean shiftKeyDown = e.isShiftDown();
boolean deselect = (controlKeyDown &&
table.isCellSelected(hitRowIndex, hitColumnIndex));
if (!controlKeyDown && !shiftKeyDown) {
clearSelection();
}
updateSelection(hitRowIndex, hitColumnIndex, deselect, !shiftKeyDown || deselect);
}
else {
Component editorComp = table.getEditorComponent();
Point componentPoint = SwingUtilities.convertPoint
(table, new Point(e.getX(), e.getY()), editorComp);
dispatchComponent = SwingUtilities.getDeepestComponentAt
(editorComp, componentPoint.x, componentPoint.y);
dispatchComponent.dispatchEvent(SwingUtilities.convertMouseEvent
(table, e, dispatchComponent));
}
}
public void mouseDragged(MouseEvent e) {
if (table.isEditing()) {
if(dispatchComponent != null)
dispatchComponent.dispatchEvent
(SwingUtilities.convertMouseEvent(table, e,
dispatchComponent));
return;
}
Point p = e.getPoint();
int hitRowIndex = table.rowAtPoint(p);
int hitColumnIndex = table.columnAtPoint(p);
if ((hitColumnIndex == -1) || (hitRowIndex == -1)) {
// Didn't hit a valid cell
return;
}
updateSelection(hitRowIndex, hitColumnIndex, false, false);
}
public void mouseReleased(MouseEvent e) {
phantomMousePressed = false;
if (table.isEditing()) {
if (dispatchComponent != null)
dispatchComponent.dispatchEvent
(SwingUtilities.convertMouseEvent(table, e,
dispatchComponent));
dispatchComponent = null;
return;
}
// Finish and clean-up from selection
}
//
// Paint Methods and support
//
public void paint(Graphics g, JComponent c) {
JTable table = (JTable) c;
Rectangle paintBounds = g.getClipBounds();
Dimension size = table.getSize();
if (table.getColumnModel() == null)
// Can't draw if I don't have a columnModel
return;
// Paint the background first
Dimension pSize = getPreferredSize(table);
Rectangle tableRect = new Rectangle(0,0,pSize.width,pSize.height);
tableRect = tableRect.intersection(paintBounds);
g.setColor(table.getParent().getBackground());
/*
// Don't repaint the whole background, only to cover it with white
// below. This causes awful flickering when scrolling if
// double buffering is turned off.
Rectangle bg[] = SwingUtilities.computeDifference(paintBounds, tableRect);
for(int i = 0; i < bg.length; i++) {
g.fillRect(bg[i].x, bg[i].y, bg[i].width, bg[i].height);
}
*/
// This caused a refresh bug, fill the whole region for now - revisit this.
g.fillRect(paintBounds.x, paintBounds.y, paintBounds.width, paintBounds.height);
// The height and width of the dirty region can somehow be negative.
// Check for this and return as this odd line segments to be drawn
// during the refreshing of the part of the display that should have
// been painted in the viewport background color when the table is smaller
// than the view.
// PENDING(philip): Ensure that these dimensions are positive in the first place.
if (tableRect.height < 0 || tableRect.width < 0) {
return;
}
Color bColor = table.getBackground();
if(bColor != null) {
g.setColor(bColor);
g.fillRect(tableRect.x, tableRect.y, tableRect.width, tableRect.height);
}
// Next draw the grid
drawGridInClipRect(tableRect, g);
// Finally draw the rows
int index;
Rectangle rowRect, intersection;
Rectangle r = g.getClipBounds();
int firstIndex = table.rowAtPoint(new Point(0, r.y));
int lastIndex = lastVisibleRow(r);
rowRect = new Rectangle(0, 0,
table.getColumnModel().getTotalColumnWidth(),
table.getRowHeight() + table.getIntercellSpacing().height);
rowRect.y = firstIndex*rowRect.height;
for (index = firstIndex; index <= lastIndex; index++) {
// draw any rows that need to be drawn
if (rowRect.intersects(tableRect)) {
intersection = rowRect.intersection(tableRect);
this.drawRowInClipRect(index, intersection, g);
}
rowRect.y += rowRect.height;
}
}
//
// Size Methods
//
public Dimension getMinimumSize(JComponent c) {
return getPreferredSize(c);
}
/** If the table is autoresizing its columns
* <code>(getAutoResizeMode() != JTable.AUTO_RESIZE_OFF)</code>
* and the JTable is also inside a scrollpane, the preferred width
* of the JTable is the width of the viewport that contains it.
* Otherwise, the preferred width is the total width of all
* of the columns. The preferred height it the number of rows
* times the preferred height (including the intercell spacing).
* <p>
* The result is that when the mode of the JTable is AUTO_RESIZE_OFF
* columns are not autoresized and a horizontal scrollbar appears
* when the total width of the columns excedes the width of the
* JScrollPane. When the mode is not AUTO_RESIZE_OFF the JTable
* autoresizes its columns continually to ensure that they fill
* the full width of the JScrollPane.
*/
public Dimension getPreferredSize(JComponent c) {
JTable table = (JTable)c;
Dimension size = new Dimension();
int mode = table.getAutoResizeMode();
Component parent = table.getParent();
if (mode != JTable.AUTO_RESIZE_OFF && parent instanceof JViewport) {
size.width = parent.getBounds().width;
table.sizeColumnsToFit(mode == JTable.AUTO_RESIZE_LAST_COLUMN);
}
else {
size.width = table.getColumnModel().getTotalColumnWidth();
}
size.height = table.getRowCount() * (table.getRowHeight() +
table.getIntercellSpacing().height);
return size;
}
public Dimension getMaximumSize(JComponent c) {
return getPreferredSize(c);
}
//
// Protected & Private Methods
//
private int lastVisibleRow(Rectangle clipRect) {
int lastIndex = table.rowAtPoint(new Point(0, clipRect.y + clipRect.height - 1));
// If the table does not have enough rows to fill the view we'll get -1.
// Replace this with the index of the last row.
if (lastIndex == -1) {
lastIndex = table.getRowCount() -1;
}
return lastIndex;
}
/**
* Draws the grid lines within <I>aRect</I>, using the grid
* color set with <I>setGridColor</I>. Draws vertical lines
* if <code>getShowVerticalLines()</code> returns true and draws
* horizontal lines if <code>getShowHorizontalLines()</code>
* returns true.
*/
protected void drawGridInClipRect(Rectangle rect, Graphics g) {
g.setColor(table.getGridColor());
if (table.getShowHorizontalLines()) {
drawHorizontalLinesInClipRect(rect, g);
}
if (table.getShowVerticalLines()) {
drawVerticalLinesInClipRect(rect, g);
}
}
/**
* This method draws horizontal lines regardless of whether the
* table is set to draw one automatically.
* Subclasses can override this method to draw grid lines
* other than the standard ones.
*/
protected void drawHorizontalLinesInClipRect(Rectangle rect, Graphics g) {
int delta = table.getRowHeight() + table.getIntercellSpacing().height;
Rectangle r = g.getClipBounds();
int firstIndex = table.rowAtPoint(new Point(0, r.y));
int lastIndex = lastVisibleRow(r);
int y = delta*firstIndex+(delta-1);
for (int index = firstIndex; index <= lastIndex; index ++) {
if ((y >= rect.y) && (y <= (rect.y + rect.height))) {
g.drawLine(rect.x, y, rect.x + rect.width - 1, y);
}
y += delta;
}
}
/**
* This method draws vertical lines regardless of whether the
* table is set to draw one automatically.
* Subclasses can override this method to draw grid lines
* other than the standard ones.
*/
protected void drawVerticalLinesInClipRect(Rectangle rect, Graphics g) {
int x = 0;
int count = table.getColumnCount();
for (int index = 0; index <= count; index ++) {
if ((x > 0) && (((x-1) >= rect.x) && ((x-1) <= (rect.x + rect.width)))){
g.drawLine(x - 1, rect.y, x - 1, rect.y + rect.height - 1);
}
if (index < count)
x += ((TableColumn)table.getColumnModel().getColumn(index)).
getWidth() + table.getIntercellSpacing().width;
}
}
/**
* Draws the cells for the row at rowIndex in the columns that intersect
* clipRect. Subclasses can override this method to customize their appearance.
*/
protected void drawRowInClipRect(int row, Rectangle rect, Graphics g) {
int column = 0;
boolean drawn = false;
Rectangle cellRect, draggedCellRect = null;
TableColumn aColumn;
int draggedColumnIndex = -1;
TableCellRenderer renderer;
Enumeration enumeration = table.getColumnModel().getColumns();
Dimension spacing = table.getIntercellSpacing();
JTableHeader header = table.getTableHeader();
// Set up the cellRect
cellRect = new Rectangle();
cellRect.height = table.getRowHeight() + spacing.height;
cellRect.y = row * cellRect.height;
// Paint the non-dragged table cells first
while (enumeration.hasMoreElements()) {
aColumn = (TableColumn)enumeration.nextElement();
cellRect.width = aColumn.getWidth() + spacing.width;
if (cellRect.intersects(rect)) {
drawn = true;
if ((header == null) || (aColumn != header.getDraggedColumn())) {
renderer = getCellRenderer(column);
Component component = prepareRenderer(renderer, table, row, column);
drawWithComponent(g, component, cellRect);
}
else {
// Draw a gray well in place of the moving column
g.setColor(table.getParent().getBackground());
g.fillRect(cellRect.x, cellRect.y,
cellRect.width, cellRect.height);
draggedCellRect = new Rectangle(cellRect);
draggedColumnIndex = column;
}
}
else {
if (drawn)
// Don't need to iterate through the rest
break;
}
cellRect.x += cellRect.width;
column++;
}
// draw the dragged cell if we are dragging
if (draggedColumnIndex != -1 && draggedCellRect != null) {
renderer = getCellRenderer(draggedColumnIndex);
if (renderer != null) {
Component component = prepareRenderer(renderer, table,
row, draggedColumnIndex);
draggedCellRect.x += header.getDraggedDistance();
// Fill the background then draw a complete grid if required
Color bColor = table.getBackground();
if (bColor != null) {
g.setColor(bColor);
g.fillRect(draggedCellRect.x, draggedCellRect.y,
draggedCellRect.width, draggedCellRect.height);
}
// Draw grid if necessary.
g.setColor(table.getGridColor());
int x1 = draggedCellRect.x;
int y1 = draggedCellRect.y;
int x2 = x1 + draggedCellRect.width - 1;
int y2 = y1 + draggedCellRect.height - 1;
// Right
if (table.getShowVerticalLines()) {
g.drawLine(x2, y1, x2, y2);
}
// Bottom
if (table.getShowHorizontalLines()) {
g.drawLine(x1, y2, x2, y2);
}
drawWithComponent(g, component, draggedCellRect);
}
}
}
protected TableCellRenderer getCellRenderer(int column) {
TableColumn tableColumn = table.getColumnModel().getColumn(column);
TableCellRenderer renderer = tableColumn.getCellRenderer();
if (renderer == null) {
Class columnClass = table.getColumnClass(column);
renderer = table.getDefaultRenderer(columnClass);
}
return renderer;
}
protected Component prepareRenderer(TableCellRenderer renderer,
JTable table, int row, int column) {
Object value = table.getValueAt(row, column);
boolean isSelected = table.isCellSelected(row, column);
boolean rowIsAnchor = (table.getSelectedRow() == row);
boolean columnIsAnchor = (table.getSelectedColumn() == column);
boolean hasFocus = (rowIsAnchor && columnIsAnchor) && table.hasFocus();
return renderer.getTableCellRendererComponent(table, value,
isSelected, hasFocus,
row, column);
}
protected void drawWithComponent(Graphics g, Component component,
Rectangle cellRect) {
// The cellRect is inset by half the intercellSpacing before drawn
Dimension spacing = table.getIntercellSpacing();
// Round so that when the spacing is 1 the cell does not draw obscure lines.
cellRect.setBounds(cellRect.x + spacing.width/2, cellRect.y + spacing.height/2,
cellRect.width - spacing.width, cellRect.height - spacing.height);
if (component.getParent() == null) {
rendererPane.add(component);
}
rendererPane.paintComponent(g, component, table, cellRect.x, cellRect.y,
cellRect.width, cellRect.height, true);
// Have to restore the cellRect back to it's orginial size
cellRect.setBounds(cellRect.x - spacing.width/2, cellRect.y - spacing.height/2,
cellRect.width + spacing.width, cellRect.height + spacing.height);
}
private void updateSelectionModel(ListSelectionModel sm, int index,
boolean deselect, boolean reAnchor) {
if (reAnchor) {
if (!deselect) {
sm.addSelectionInterval(index, index);
}
else {
sm.removeSelectionInterval(index, index);
// sm.setAnchorSelectionIndex(index);
return;
}
}
sm.setLeadSelectionIndex(index);
}
private void updateSelection(int rowIndex, int columnIndex,
boolean deselect, boolean reAnchor) {
// Autoscrolling support.
Rectangle cellRect = table.getCellRect(rowIndex, columnIndex, false);
if (cellRect != null) {
table.scrollRectToVisible(cellRect);
}
ListSelectionModel rsm = table.getSelectionModel();
ListSelectionModel csm = table.getColumnModel().getSelectionModel();
// Update column selection model
updateSelectionModel(csm, columnIndex, deselect, reAnchor);
// Update row selection model
updateSelectionModel(rsm, rowIndex, deselect, reAnchor);
}
// Its faster to deselect, rather than clear.
private void clearSelection() {
clearSelection(table.getColumnModel().getSelectionModel());
clearSelection(table.getSelectionModel());
}
// Its faster to deselect, rather than clear.
private void clearSelection(ListSelectionModel sm) {
sm.removeSelectionInterval(sm.getMinSelectionIndex(), sm.getMaxSelectionIndex());
}
} // End of Class BasicTableUI